//
// Created by Administrator on 2024/7/31.
//

#ifndef TYPES_H
#define TYPES_H

#include <boost/algorithm/hex.hpp>

#include "concepts.hpp"
#include "error.hpp"
#include "logger.hpp"
#include "macros.hpp"
#include "serializer.hpp"

namespace common {
    template<BasicType T>
    class Container final : public Sizeable, public Serializable, public ToString {
    public:
        T *data;
        size_t _size = 0;

        Container() {
            data = new T[_size];
        }

        explicit Container(const size_t size) : _size(size) {
            data = new T[_size];
        }

        Container(std::initializer_list<T> list) : _size(list.size()) {
            data = new T[_size];
            std::copy(list.begin(), list.end(), data);
        }

        Container(const T *data) {
            static_assert(typeid(T) == typeid(char));
            _size = strlen(data) + 1;
            this->data = new T[_size];
            memcpy(this->data, data, _size);
        }

        Container(const T *data, const size_t size) : _size(size) {
            this->data = new T[_size];
            memcpy(this->data, data, size);
        }

        Container(MessagePackReader &reader, const size_t size) : _size(size) {
            this->data = new T[_size];
            deserialize(reader);
        }

        Container(const Container &other) : _size(other._size) {
            data = new T[_size];
            memcpy(data, other.data, _size);
        }

        Container &operator=(const Container &other) {
            if (this != &other) {
                delete[] data;
                _size = other._size;
                data = new T[_size];
                memcpy(this->data, other.data, _size);
            }
            return *this;
        }


        T &operator[](size_t index) {
            if (index >= _size) {
                ERR(Logger::logger(), "Index out of range!");
                throw Error(error::IndexOutOfRange, "Index out of range!");
            }
            return data[index]; // ????????????????
        }


        T operator[](size_t index) const {
            if (index >= _size) {
                ERR(Logger::logger(), "Index out of range!");
                throw Error(error::IndexOutOfRange, "Index out of range!");
            }
            return data[index]; // ????????????????
        }

        void serialize(MessagePackWriter &writer) override {
            writer.writePointer(data, _size);
        }

        void deserialize(MessagePackReader &reader) override {
            if (_size != 0) reader.readPointer(data, _size);
            else {
                delete[] data;
                auto str = reader.readString();
                _size = str.size()+1;
                data = new T[_size];
                memcpy(data, str.data(), _size);
            }
        }

        ~Container() override {
            delete[] data;
        }

        [[nodiscard]] std::string toString() const override {
            if constexpr (std::is_same<T, char>::value) {
                return "{" + std::string(data, _size) + "}";
            }
            return toHex(data, _size);
        }

    protected:
        [[nodiscard]] size_t size() const override { return _size; }
    };


    template<BasicType T>
    using container_t = std::shared_ptr<Container<T>>;


    struct BcdTime final : Sizeable, Serializable, ToString {
        uint8_t year{}, month{}, day{}, hour{}, minute{}, second{};

        BcdTime() {
            const auto now = std::chrono::system_clock::now();
            const std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);
            std::tm tm = *std::localtime(&now_time_t);

            year = tm.tm_year;
            month = tm.tm_mon;
            day = tm.tm_mday;
            hour = tm.tm_hour;
            minute = tm.tm_min;
            second = tm.tm_sec;
        }

        BcdTime(const uint8_t year, const uint8_t month, const uint8_t day, const uint8_t hour, const uint8_t minute,
                const uint8_t second)
            : year(year),
              month(month),
              day(day),
              hour(hour),
              minute(minute),
              second(second) {
        }

        ~BcdTime() override = default;

        void serialize(MessagePackWriter &writer) override {
            APPLY(writer.writeByte, ;, year, month, day, hour, minute, second);
        }

        void deserialize(MessagePackReader &reader) override {
            APPLY(reader.readUint, ;, year, month, day, hour, minute, second);
        }

        [[nodiscard]] std::string toString() const override {
            tm t{second, minute, hour, day, month, year};
            const auto time = mktime(&t);
            return std::string{ctime(&time)};
        }

        IMPLEMENT_SIZEABLE(year, month, day, hour, minute, second)
    };


    template<size_t N>
    struct Bcd final : Sizeable, Serializable, ToString {
        static_assert(N % 2 == 0, "N must be even");

        uint8_t bytes[N/2]{};

        IMPLEMENT_SIZEABLE(bytes);
        IMPLEMENT_TOSTRING(bytes);

        Bcd() = default;

        Bcd(const std::string &human_friendly) {
            for (int i = 0; i < N; ++i) {
                uint8_t high{0}, low{0};
                if (human_friendly.size() > i) high = static_cast<uint8_t>(std::stoi(human_friendly.substr(i++, 1)));
                if (human_friendly.size() > i) low = static_cast<uint8_t>(std::stoi(human_friendly.substr(i, 1)));
                bytes[i/2] = high << 4 | low;
            }
        }

        Bcd(const Bcd &other) {
            memcpy(bytes, other.bytes, N/2);
        }

        void serialize(MessagePackWriter &writer) override {
            writer.writeBytes(bytes);
        }

        void deserialize(MessagePackReader &reader) override {
            reader.readBytes(bytes);
        }
    };


    template<size_t N>
    struct Bytes final : Sizeable, Serializable, ToString {
        uint8_t bytes[N]{};

        Bytes() = default;

        // ReSharper disable once CppNonExplicitConvertingConstructor
        Bytes(const char *hex_data) {
            // NOLINT(*-explicit-constructor)
            const char *p = hex_data;
            // Skip leading '0x' if present
            if (*p == '0' && (p[1] == 'x' || p[1] == 'X')) {
                p += 2;
            }
            std::vector<uint8_t> decoded_bytes;
            boost::algorithm::unhex(p, std::back_inserter(decoded_bytes));
            const auto to_fill = N - decoded_bytes.size();
            decoded_bytes.insert(decoded_bytes.begin(), to_fill, 0);

            // Ensure that we have enough bytes
            // assert(decoded_bytes.size() >= N);

            // Copy the last N bytes into our array
            std::memcpy(bytes, decoded_bytes.data(), N);
        }

    protected:
        [[nodiscard]] size_t size() const override { return N; }

    public:
        ~Bytes() override = default;

        void serialize(MessagePackWriter &writer) override {
            writer.writeBytes(bytes);
        }

        void deserialize(MessagePackReader &reader) override {
            reader.readBytes(bytes);
        }

        [[nodiscard]] std::string toString() const override {
            return "{" + toHex(bytes, N) + "}";
        }
    };
}

#endif //TYPES_H
